/*
 *  * Debug.h
 *   */
#ifndef _T_DEBUG_H
#define _T_DEBUG_H

#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdint.h>
#if defined(assert)
#undef assert
#endif

#if defined(NODEBUG)
#define assert(EX) ((void)0)
#else
#define assert(EX)  ((EX)?((void)0):xassert( # EX , __FILE__, __LINE__))
#endif

void xassert(const char *, const char *, int);

/* context-based debugging, the actual type is subject to change */
typedef int Ctx;
Ctx ctx_enter(const char *descr);
void ctx_exit(Ctx ctx);

/* defined debug section limits */
#define MAX_DEBUG_SECTIONS 128

/* defined names for Debug Levels */
#define DBG_CRITICAL    0   /**< critical messages always shown when they occur */
#define DBG_IMPORTANT   1   /**< important messages always shown when their section is being checked */
/* levels 2-8 are still being discussed amongst the developers */
#define DBG_DATA    9   /**< output is a large data dump only necessary for advanced debugging */

class Debug
{

    public:
        static char *debugOptions;
        static char *tiny_log;
        static int rotateNumber;
        static int Levels[MAX_DEBUG_SECTIONS];
        static int level;
        static int log_stderr;
        static bool log_syslog;

        static std::ostream &getDebugOut();
        static void finishDebug();
        static void parseOptions(char const *);

    private:
        //Hack: replaces global ::xassert() to debug debugging assertions
        static void xassert(const char *msg, const char *file, int line);

        static std::ostringstream *CurrentDebug;
        static int TheDepth;  //level of nested debugging calls
};

extern FILE *debug_log;

/* Debug stream */
#define debugs(SECTION, LEVEL, CONTENT) \
    do { \
        if ((Debug::level = (LEVEL)) <= Debug::Levels[SECTION]) { \
            std::ostream &_dbo=Debug::getDebugOut(); \
            if (Debug::level > DBG_IMPORTANT) \
            _dbo <<__FILE__<<"("<<__LINE__<<") "<<__FUNCTION__<<": "; \
            _dbo << CONTENT; \
            Debug::finishDebug(); \
        } \
    } while (/*CONSTCOND*/ 0)

/** stream manipulator which does nothing.
 * \deprecated Do not add to new code, and remove when editing old code
 *
 * Its purpose is to inactivate calls made following previous debugs()
 * guidelines such as
 * debugs(1,2, HERE << "some message");
 *
 * His former objective is now absorbed in the debugs call itself
 */
inline std::ostream&
HERE(std::ostream& s)
{
    return s;
}

/*
 * MYNAME is for use at debug levels 0 and 1 where HERE is too messy.
 *
 * debugs(1,1, MYNAME << "WARNING: some message");
 */
#define MYNAME __FUNCTION__ << " "

/* some uint8_t do not like streaming control-chars (values 0-31, 127+) */
inline std::ostream& operator <<(std::ostream &os, const uint8_t d)
{
    return (os << (int)d);
}

/* Legacy debug style */
#define do_debug(SECTION, LEVEL)   ((Debug::level = (LEVEL)) <= Debug::Levels[SECTION])
#define old_debug(SECTION, LEVEL)  if do_debug((SECTION), (LEVEL)) _db_print

/* Legacy debug function definitions */
void _db_init(const char *logfile, const char *options);
void _db_print(const char *,...);
void _db_rotate_log(void); // for one hour rotate log

#endif /* _T_DEBUG_H */

